package io.jsen.meta.diff.watch;


import io.jsen.meta.diff.obj.MetaObject;
import io.jsen.meta.diff.obj.complex.MetaCollection;
import io.jsen.meta.diff.obj.complex.MetaComplex;
import io.jsen.meta.diff.obj.complex.MetaMap;
import io.jsen.meta.diff.obj.special.MetaDelete;
import io.jsen.meta.diff.obj.special.MetaNull;
import io.jsen.meta.diff.support.MetaModifier;

import static io.jsen.meta.diff.support.MetaChangeModifier.isNoChange;

/**
 * @author jsen
 * @date 2021/4/7
 */
public class MetaChangeWalker {

    public static void walk(MetaObject<?, ?> metaObject, Walker walker) {
        if (!isDiffed(metaObject)) {
            throw new IllegalArgumentException("对象未完成diff");
        }
        innerWalk(metaObject, walker);
    }

    private static void innerWalk(MetaObject<?, ?> metaObject, Walker walker) {
        if (!walker.stopWalk(metaObject)) {
            if (metaObject instanceof MetaComplex) {
                walkOne(metaObject, walker);
                ((MetaComplex) metaObject).getVal().forEach((k, meta) -> innerWalk(meta, walker));
            } else if (metaObject instanceof MetaCollection) {
                walkOne(metaObject, walker);
                ((MetaCollection) metaObject).getVal().forEach((meta) -> innerWalk(meta, walker));
            } else if (!(metaObject instanceof MetaDelete) &&
                    !(metaObject instanceof MetaNull)) {
                walkOne(metaObject, walker);
            }
        }
    }

    private static void walkOne(MetaObject<?, ?> metaObject, Walker walker) {
        if (!isNoChange(metaObject.getModifier())) {
            walker.print(metaObject);
        }
    }

    private static boolean isDiffed(MetaObject<?, ?> metaObject) {
        if (metaObject instanceof MetaComplex) {
            return ((MetaComplex) metaObject).getVal().values().stream().allMatch(MetaChangeWalker::isDiffed);
        }
        if (metaObject instanceof MetaCollection) {
            return ((MetaCollection) metaObject).getVal().stream().allMatch(MetaChangeWalker::isDiffed);
        }
        if (metaObject instanceof MetaMap) {
            return ((MetaMap<?>) metaObject).getVal().values().stream().allMatch(MetaChangeWalker::isDiffed);
        }
        return MetaModifier.isCompared(metaObject.getModifier());
    }

    public interface Walker {
        /**
         * 遇到这个停止向下遍历，也不遍历这个对象
         *
         * @param metaObject metaObject
         * @return 是否停止遍历
         */
        boolean stopWalk(MetaObject<?, ?> metaObject);

        /**
         * 遍历对象，打印
         *
         * @param metaObject metaObject
         */
        void print(MetaObject<?, ?> metaObject);
    }
}
